﻿#define _CRT_SECURE_NO_WARNINGS 1

// 1.旋转字符串
// 方法一：有缺陷（有用例过不了）
class Solution {
public:
	bool solve(string A, string B) {
		if (A == B)
			return true;
		int n1 = A.size();
		int n2 = B.size();
		int l1 = 0, l2 = 0;
		while (A[l1] != B[l2])
			l2++;
		int tmp = l2;
		while (l1 < n1 && l2 < n2 && A[l1] == B[l2])
		{
			l1++;
			l2++;
		}
		if (l2 == n2)
		{
			l2 = 0;
			while (l1 < n1 && l2 < n2 && A[l1] == B[l2])
			{
				l1++;
				l2++;
			}
			if (l1 == n1 && l2 == tmp)
				return true;
		}
		else
			return false;
		return false;
	}
};

// 方法二：直接倍增，让 A 字符串倍增，然后查找 B 字符串
// 如果 A 字符串能够旋转之后得到 B 字符串的话，在 A 字符串倍增之后的新串中，⼀定是可以找到B 字符串的。
class Solution
{
public:
	bool solve(string A, string B)
	{
		if (A.size() != B.size()) 
			return false;
		return 
			(A + A).find(B) != -1;
	}
};

// 2.合并k个已排序的链表
// 方法一：直接将所有值存入数组，然后排序完后插回链表即可
class Solution {
public:
	vector<int> st;
	ListNode* mergeKLists(vector<ListNode*>& lists) {
		for (int i = 0; i < lists.size(); ++i)
		{
			ListNode* tmp = lists[i];
			while (tmp != nullptr)
			{
				st.push_back(tmp->val);
				tmp = tmp->next;
			}
		}
		sort(st.begin(), st.end());
		ListNode* head = new ListNode(0);
		ListNode* cur = head;
		for (int i = 0; i < st.size(); ++i)
		{
			ListNode* tmp = new ListNode(st[i]);
			cur->next = tmp;
			cur = cur->next;
		}
		return head->next;
	}
};
// 方法二：利用堆将链表进行排序
class Solution
{
	struct cmp
	{
		bool operator()(ListNode* l1, ListNode* l2)
		{
			return l1->val > l2->val;
		}
	};

public:
	ListNode* mergeKLists(vector<ListNode*>& lists)
	{
		priority_queue<ListNode*, vector<ListNode*>, cmp> heap; // ⼩根堆
		for (auto head : lists)
		{
			if (head != nullptr)
			{
				heap.push(head);
			}
		}
		ListNode* ret = new ListNode(0);
		ListNode* prev = ret;
		while (heap.size())
		{
			ListNode* t = heap.top();
			heap.pop();
			prev = prev->next = t;
			if (t->next != nullptr)
			{
				heap.push(t->next);
			}
		}
		return ret->next;
	}
};

// 3.滑雪
// 递归
#include <iostream>
using namespace std;

const int N = 110;
int n, m;
int ret;
int arr[N][N];
bool vis[N][N];
int dx[4] = { 0, 0, 1, -1 };
int dy[4] = { 1, -1, 0, 0 };

bool Check(int x, int y)
{
	for (int i = 0; i < 4; ++i)
	{
		int a = x + dx[i];
		int b = y + dy[i];
		if (a >= 1 && a <= n && b >= 1 && b <= m && !vis[a][b] && arr[x][y] > arr[a][b])
			return true;
	}
	return false;
}

void DFS(int x, int y, int path)
{
	if (!Check(x, y))
	{
		ret = max(ret, path);
		return;
	}
	for (int i = 0; i < 4; ++i)
	{
		int a = x + dx[i];
		int b = y + dy[i];
		if (a >= 1 && a <= n && b >= 1 && b <= m && !vis[a][b] && arr[x][y] > arr[a][b])
		{
			DFS(a, b, path + 1);
		}
	}
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			cin >> arr[i][j];
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			DFS(i, j, 1);
	cout << ret << endl;
	return 0;
}
// 递归-》记忆化搜索
#include <iostream>
using namespace std;
const int N = 110;
int n, m;
int arr[N][N];
int dp[N][N];
int dx[4] = { 0, 0, 1, -1 };
int dy[4] = { 1, -1, 0, 0 };
int dfs(int i, int j)
{
	if (dp[i][j]) return dp[i][j];
	int len = 1;
	for (int k = 0; k < 4; k++)
	{
		int x = i + dx[k], y = j + dy[k];
		if (x >= 1 && x <= n && y >= 1 && y <= m && arr[x][y] < arr[i][j])
		{
			len = max(len, 1 + dfs(x, y));
		}
	}
	dp[i][j] = len;
	return len;
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			cin >> arr[i][j];
		}
	}
	int ret = 1;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			ret = max(ret, dfs(i, j));
		}
	}
	cout << ret << endl;
	return 0;
}